/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>

/*Node creation functions*/

u32 data_type_size[] = {1, 1, 1, 2, 4, 8, 4};

char *usr_data_type[] = {"void", "char", "byte", "word", "dword", "qword", "int"};

static u32 null_data;

void *create_data (u8 data_type, u32 count)
{
	if(data_type == DATA_TYPE_VOID)
		return &null_data;
	return malloc( data_type_size[data_type] * count );
}

struct node *create_node(u16 type, u8 data_type, u32 count, char *name, int nr_clients)
{
	struct node *n;
	int i;
	//printk("create_node: %d %d %d %s %d", type, data_type, count, name, nr_clients);
	n = (struct node *)malloc(sizeof(struct node) + nr_clients * sizeof(struct node *));
	n->type = type;
	n->data_type = data_type;
	strcpy(n->name, name);
	n->data = create_data(data_type, count);
	n->server = NULL;
	n->count = count;
	n->nr_clients = nr_clients;
	for(i=0; i<nr_clients; i++)
		n->client[i] = NULL;
	return n;
}

struct node *create_mapped_node(struct node *n, char *name, int nr_clients)
{
	int i;
	struct node *nn;
	nn = (struct node *)malloc(sizeof(struct node) + nr_clients * sizeof(struct node *));
	memcpy(nn, n, sizeof(struct node));
	strcpy(nn->name, name);
	nn->nr_clients = nr_clients;
	nn->server = NULL; //We don't want to inherit the server
	for(i = 0; i < nr_clients; i++)
		nn->client[i] = NULL;
	return nn;
}


#define min(a,b) a < b ? a : b


void do_transfer(struct node *to, struct node *from)
{
	u32 transfer_count;
	if(!(to->type & TYPE_VDATA_NO_INCOMING) && !(from->type & TYPE_VDATA_NO_OUTGOING)) {
		if(to->data != from->data) {
			transfer_count = min(to->count, from->count);
			memcpy(to->data, from->data, transfer_count * data_type_size[to->data_type]);
		}
	}
}


int select_handler(struct node *node, struct node *referrer, int code)
{
	u32 i;
	struct select *select;
	int select_flag = 0;
	select = (struct select *)node;
	if(code == REFER_CLIENT) {
		if(referrer->data_type == DATA_TYPE_CHAR) {
			select_flag = 0;
			for(i = 0; i < (select->nr_clients-1); i++) {
				if(!strcmp(select->client[i]->data, referrer->data)) {
					service(select->client[i], (struct node *)select, REFER_CLIENT);
					select_flag = 1;
				}
			}
			if(!select_flag) {
				service(select->client[select->nr_clients-1], (struct node *)select, REFER_CLIENT);
			}
		}
	}
	return 0;
}


int cmdline_handler(struct node *node, struct node *referrer, int code)
{
	struct cmdline *cmdline;
	cmdline = (struct cmdline *)node;
	if(code == REFER_CLIENT) {
		if(referrer->data_type == DATA_TYPE_CHAR) {
			cmdline->input = referrer->data; //Store the string pointer
			cmdline->cur_no = 0; //Refer the parameter counter
		} else if(referrer->data_type == DATA_TYPE_INT) { //set cur_no
			cmdline->cur_no = GET_INT(referrer);
		}
	} else {
		if(referrer->data_type == DATA_TYPE_CHAR) { //Read a string from the commandline string
			getstringparameter(cmdline->input, referrer->data, ' ', cmdline->cur_no++);
		} else if(referrer->data_type == DATA_TYPE_INT) { //Read a number from the commandline string
			*(u32 *)referrer->data = getparam(cmdline->input, ' ', cmdline->cur_no++);
		}
	}
	return 0;
}

int mathvm_handler(struct node *node, struct node *referrer, int code)
{
	struct mathvm *mathvm;
	mathvm = (struct mathvm *)node;
	execute_mathvm(mathvm); //This should do all result calculation and transfers
	return 0;
}

int fsif_handler(struct node *node, struct node *referrer, int code)
{
	struct fsif *f;
	struct node_fs *n;
	f = (struct fsif *)node;
	char path[MAX_FS_NAME_LEN];
	if(code == REFER_SERVER) {
		if(referrer->data_type == DATA_TYPE_CHAR) {
			strcpy(path, "");
			fs_path(path, f->fs);
			strcpy(referrer->data, path);
		}
	} else {
		if(referrer->data_type == DATA_TYPE_CHAR) {
			n = fs_get_relative(f->fs, referrer->data);
			if(n)
				f->fs = n;
			else
				printk("fsif: Cannot locate %s\n", referrer->data);
		}
	}
	return 0;
}


struct builtin_handler {
	int (*handler)(struct node *node, struct node *referrer, int code);
}builtin_handlers[MAX_BUILTIN_HANDLERS];



void service_server(struct node *node)
{
	//Check if the node referred to is a vdata. Otherwise just service it(it is a builtin then)
	#ifdef DEBUG_EXECUTION
		printk("--server = %s\n", node->server->name);
	#endif
	if(node->data_type == node->server->data_type && node->type & TYPE_VDATA && node->server->type & TYPE_VDATA_BUILTINSMASK) {
		service(node->server, node, REFER_SERVER);
		do_transfer(node, node->server);
	} else {
		service(node->server, node, REFER_SERVER);
	}
}

void service_client(struct node *node, u32 i)
{
	//Check if the node referred to is a vdata. Otherwise just service it(it is a builtin then)
	#ifdef DEBUG_EXECUTION
		printk("--client%u = %s\n", i, node->client[i]->name);
	#endif
	if(node->data_type == node->client[i]->data_type && node->type & TYPE_VDATA && node->client[i]->type & TYPE_VDATA) {
		do_transfer(node->client[i], node);
		service(node->client[i], node, REFER_CLIENT);
	} else {
		service(node->client[i], node, REFER_CLIENT);
	}
}


void service(struct node *node, struct node *referrer, int code)
{
	u32 transfer_count, i;
	struct node_fs *fs;
	int select_flag = 0;
	//If it is a builtin, handle it by calling the handlers
	if(node->type & TYPE_VDATA_BUILTINSMASK) {
		#ifdef DEBUG_EXECUTION
			printk("Handle builtin(%u): %s\n", (node->type & TYPE_VDATA_BUILTINSMASK) >> 8, node->name);
		#endif
		builtin_handlers[(node->type & TYPE_VDATA_BUILTINSMASK) >> 8].handler(node, referrer, code);
		return;
	}
	//Get data from server first
	#ifdef DEBUG_EXECUTION
		printk("service : %s\n", node->name);
	#endif
	loop:
	if(node->server) {
		if(node->server->type & TYPE_FSMASK) { //Read from a kernel mode system
			if(!(node->type & TYPE_VDATA_NO_INCOMING)) {
				fs = (struct node_fs *)node->server;
				#ifdef DEBUG_EXECUTION
					printk("Read from %s(fs)\n", fs->name);
				#endif
				transfer_count = (node->count * data_type_size[node->data_type]) / (fs->op_unit);
				fs->read(fs, node->data, 0, transfer_count);
			}
		} else {
			service_server(node);
		}
	}
	for(i=0; i<node->nr_clients; i++) {
		//printk("--client %s\n", node->client[i]->name);
		if(node->client[i]) {
			if(node->client[i]->type & TYPE_FSMASK) { //Read from a kernel mode system
				if(!(node->type & TYPE_VDATA_NO_OUTGOING)) {
					fs = (struct node_fs *)node->client[i];
					#ifdef DEBUG_EXECUTION
						printk("Write to %s(fs)\n", fs->name);
					#endif
					transfer_count = (node->count * data_type_size[node->data_type]) / (fs->op_unit);
					fs->write(fs, node->data, 0, transfer_count);
				}
			} else {
				service_client(node, i);
			}
		} else
			break;
	}
	if(node->type & TYPE_VDATA_LOOP) {
		goto loop;
	}
}

struct node *fs_cast(char *name)
{
	return (struct node *)fs_get(name);
}


u32 find_type(char *usr_name)
{
	u32 i;
	for(i=0; i<NR_DATA_TYPES; i++) {
		if(!strcmp(usr_name, usr_data_type[i]))
			return i;
	}
	return NR_DATA_TYPES;
}




/*
String formats: 

new <count> <data_type> <name> <number of links>
Create a new vdata node. The flags can be modified using:

flags <i><o><l>
i -> no incoming
o -> no outgoing
l -> loop on this node

client <link number> <node name>
Create a client link from the current editing node to the <node name>. This is where lookup of the created nodes is required from the temp array

string <data>
Add string to the node that is currently being edited.

begin <node_name>
Begin servicing node_name

*/
void sys(char *desc)
{
	char param[MAX_FS_NAME_LEN], param1[MAX_FS_NAME_LEN], name[MAX_FS_NAME_LEN];
	struct node *n, *t;
	u32 nr, data_type, nr_links, k, i;
	getstringparameter(desc, param, ' ', 0);
	if(!strcmp(param, "mathvm")) {
		struct mathvm *mathvm;
		getstringenv(desc, param, '\"', '\"', &k); //Get expression
		getstringparameter(desc+k, param1, ' ', 1); //Get name
		nr = getparam(desc+k, ' ', 2); //Get number of clients
		
		
		mathvm = (struct mathvm *)parse_expression(param, nr);
		
		
		mathvm->type = TYPE_VDATA_MATHVM;
		
		strcpy(mathvm->name, param1);
		mathvm->nr_clients = nr;
		
		
		app_attach_temp((struct node *)mathvm);
		set_editing_node((struct node *)mathvm);
		
		
	} else if(!strcmp(param, "new")) {
		nr = getparam(desc, ' ', 1);
		getstringparameter(desc, param1, ' ', 2);
		if(!strcmp(param1, "byte")) {
			data_type = DATA_TYPE_BYTE;
		} else if(!strcmp(param1, "word")) {
			data_type = DATA_TYPE_WORD;
		} else if(!strcmp(param1, "dword")) {
			data_type = DATA_TYPE_DWORD;
		} else if(!strcmp(param1, "int")) {
			data_type = DATA_TYPE_INT;
		} else if(!strcmp(param1, "char")) {
			data_type = DATA_TYPE_CHAR;
		} else if(!strcmp(param1, "void")) {
			data_type = DATA_TYPE_VOID;
		} else {
			printk("sys: %s: Not a valid data type\n", param1);
			return;
		}
		getstringparameter(desc, name, ' ', 3);
		nr_links = getparam(desc, ' ', 4);
		n = create_node(TYPE_VDATA, data_type, nr, name, nr_links);
		if(!n) {
			printk("sys: Node creation failed\n");
			return;
		}
		app_attach_temp(n);
		set_editing_node(n);
	} else if(!strcmp(param, "fsif")) {
		struct fsif *f;
		getstringparameter(desc, param, ' ', 1);
		f = (struct fsif *)malloc(sizeof(struct fsif));
		strcpy(f->name, param);
		f->fs = fs_get("/"); //Initialize to root
		f->type = TYPE_VDATA_FSIF;
		app_attach_temp((struct node *)f);
	} else if(!strcmp(param, "mapped")) {
		getstringparameter(desc, param, ' ', 1); //This is the name of the node which this node will be mapped to..
		getstringparameter(desc, param1, ' ', 2); //This is the name of the newly created node.
		nr = getparam(desc, ' ', 3);
		t = app_find_node(param);
		if(!t) {
			printk("sys: Node not found(%s)\n", param);
			return;
		}
		n = create_mapped_node(t, param1, nr);
		if(!n) {
			printk("sys: Node creation failed\n");
			return;
		}
		app_attach_temp(n);
		set_editing_node(n);
	} else if(!strcmp(param, "argparser")) {
		getstringparameter(desc, param, ' ', 1); //Name of the node
		struct cmdline *n;
		n = (struct cmdline *)malloc(sizeof(struct cmdline));
		n->type = TYPE_VDATA_CMDLINE;
		strcpy(n->name, param);
		n->input = NULL;
		n->cur_no = 0;
		app_attach_temp((struct node *)n);
	} else if(!strcmp(param, "select")) {
		struct select *select;
		getstringparameter(desc, name, ' ', 1);
		nr_links = getparam(desc, ' ', 2);
		select = (struct select *)malloc(sizeof(struct select) + nr_links * sizeof(struct node *));
		select->type = TYPE_VDATA_SELECT;
		select->nr_clients = nr_links;
		strcpy(select->name, name);
		for(i = 0; i < nr_links; i++) {
			select->client[i] = NULL;
		}
		n = (struct node *)select;
		if(!n) {
			printk("sys: Node creation failed\n");
			return;
		}
		app_attach_temp(n);
		set_editing_node(n);
	} else if(!strcmp(param, "flags")) {
		getstringparameter(desc, param, ' ', 1);
		k = strlen(param);
		n = get_editing_node();
		if(!n) {
			printk("sys: Editing node not set\n");
			return;
		}
		for(i = 0; i < k; i++) {
			if(param[i] == 'l') {
				n->type |= TYPE_VDATA_LOOP;
			} else if(param[i] == 'i') {
				n->type |= TYPE_VDATA_NO_INCOMING;
			} else if(param[i] == 'o') {
				n->type |= TYPE_VDATA_NO_OUTGOING;
			} else {
				printk("sys: Invalid flag: %c\n", param[i]);
			}
		}
	} else if(!strcmp(param, "client")) {
		struct mathvm *m;
		struct select *s;
		getstringparameter(desc, param, ' ', 2);
		n = get_editing_node();
		if(!n) {
			printk("sys: Editing node not set\n");
			return;
		}
		k = getparam(desc, ' ', 1);
		if(param[0] == '/') {
			t = fs_cast(param);
			if(!t) {
				printk("sys(%s): Client: FS node %s was not found\n", n->name, param);
			} else {
				if(n->type == TYPE_VDATA_MATHVM) {
					m = (struct mathvm *)n;
					m->client[k] = t;
				} else if(n->type == TYPE_VDATA_SELECT) {
					s = (struct select *)n;
					s->client[k] = t;
				} else
					n->client[k] = t;
			}
		} else {
			t = app_find_node(param);
			if(!t) {
				printk("sys(%s): Client: Node %s was not found\n", n->name, param);
			} else {
				if(n->type == TYPE_VDATA_MATHVM) {
					m = (struct mathvm *)n;
					m->client[k] = t;
				} else if(n->type == TYPE_VDATA_SELECT) {
					s = (struct select *)n;
					s->client[k] = t;
				} else
					n->client[k] = t;
			}
		}
	} else if(!strcmp(param, "server")) {
		getstringparameter(desc, param, ' ', 1);
		n = get_editing_node();
		if(!n) {
			printk("sys: Editing node not set\n");
			return;
		}
		if(param[0] == '/') {
			t = fs_cast(param);
			if(!t) {
				printk("sys(%s): Server: FS node %s was not found\n", n->name, param);
			} else {
				n->server = t;
			}
		} else {
			t = app_find_node(param);
			if(!t) {
				printk("sys(%s): Server: Node %s was not found\n", n->name, param);
			} else {
				n->server = t;
			}
		}
	} else if(!strcmp(param, "show")) {
		getstringparameter(desc, param, ' ', 1); //This is the node
		if(!strlen(param))
			n = get_editing_node();
		else
			n = app_find_node(param);
		printk("Show: %s\n", n->name);
		if(n->server) {
			printk("--server = %s\n", n->server->name);
		}
		if(n->nr_clients > 0) {
			for(i = 0; i < n->nr_clients; i++) {
				printk("--client%u = %s\n", i, n->client[i]->name);
			}
		}
		if(n->data_type == DATA_TYPE_CHAR) {
			printk("data = %s\n", n->data);
		} else if(n->data_type == DATA_TYPE_INT) {
			printk("data = %u\n", GET_INT(n));
		}
	} else if(!strcmp(param, "set_editing_node")) {
		getstringparameter(desc, param, ' ', 1);
		n = app_find_node(param);
		set_editing_node(n);
	} else if(!strcmp(param, "string")) {
		//Currently only handling string data and integers
		n = get_editing_node();
		if(!n) {
			printk("sys: Editing node not set\n");
			return;
		}
		getstringenv(desc, param, '\"', '\"', &k);
		strcpy(n->data, param);
	} else if(!strcmp(param, "integer")) {
		n = get_editing_node();
		if(!n) {
			printk("sys: Editing node not set\n");
			return;
		}
		*((u32 *)n->data) = getparam(desc, ' ', 1);
	} else if(!strcmp(param, "app_run")) {
		getstringparameter(desc, param, ' ', 1);
		app_run(param);
		printk("\nsys: Running application %s\n", param);
	} else if(!strcmp(param, "app_set_start")) {
		getstringparameter(desc, param, ' ', 1);
		t = app_find_node(param);
		if(!t) {
			printk("sys: Node not found\n");
			return;
		}
		app_set_start_node(t);
	} else if(!strcmp(param, "app_create")) {
		getstringparameter(desc, param, ' ', 1);
		getstringparameter(desc, param1, ' ', 2);
		app_create(param, param1); //This sets set_editing
	} else if(!strcmp(param, "set_editing")) {
		getstringparameter(desc, param, ' ', 1);
		set_editing(param);
	} else if(!strcmp(param, "tree")) {
		getstringparameter(desc, param, ' ', 1);
		fs_tree(param);
	} else if(!strcmp(param, "ls")) {
		struct node_fs *fs;
		getstringparameter(desc, param, ' ', 1);
		fs_iterator_parse(&fs, param);
		if(fs) {
			fs_enumerate(fs);
			fs_iterator_get_child(&fs);
			while(fs) {
				printk("%s\n", fs->name);
				fs_iterator_get_next(&fs);
			}
		}
	} else {
		printk("sys: Unsupported command (%s)\n", desc);
	}
}

int sys_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	sys(buf);
	return 0;
}

struct node_fs sys_fs = {
	.type = TYPE_FS_CLIENT,
	.init = dummy,
	.deinit = dummy,
	.name = "sys",
	.read = dummy,
	.write = sys_write,
	.enumerate = dummy,
	.notify = dummy,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT
};

int init_sys_fs()
{
	//Start the fs node of sys
	fs_link("/", &sys_fs);
	return 0;
}

char vise_logo[] = "\n\n\
_|      _|  _|    _|_|_|  _|_|_|_|  \n\
_|      _|      _|        _|        \n\
_|      _|  _|    _|_|    _|_|_|    \n\
  _|  _|    _|        _|  _|        \n\
    _|      _|  _|_|_|    _|_|_|_|  \n\n\n";



int main(struct node_fs *this)
{
	//u8 buf[512];
	struct node_fs *driver;
	init_class();
	init_timer_fs();
	init_event_ss();
	init_driver_fs();
	init_drivers();
	
	//Start all drivers
	
	printk("Starting drivers...\n");
	list_for((fs_get("/driver")->links), driver) {
		driver->write(driver, NULL, DRIVER_START, 0);
	}
	
	init_user();
	init_printk();
	
	
	builtin_handlers[1].handler = select_handler;
	builtin_handlers[2].handler = fsif_handler;
	builtin_handlers[3].handler = cmdline_handler;
	builtin_handlers[4].handler = mathvm_handler;
	
	init_sys_fs();
	
	printk("Welcome to ViSE 0.0.2\n");
	//printk(vise_logo);
	
	/*printk("Test begins..\n");
	fs_read("/class/storage/hd0_p0", buf, 0, 1);
	debug_memory(buf, 512);*/
	
	
	setup_vshell();
	
	fs_move("/sched/waiting", "/sched/tasks/main");
	fs_write("/sched", NULL, 0, 0);
	while(1);
}


void vinit()
{
	init_malloc();
	init_sched();
}
